package com.example.util;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.WeekFields;
import java.util.Date;

public class DateUtils {

    public static String ONLY_DATE_FORMAT = "yyyy-MM-dd";
    public static String PURE_DATE_FORMAT = "yyyyMMdd";
    public static String MONTH_FORMAT = "yyyyMM";
    public static String YEAR_WEEK_FORMAT = "YYYY'W'ww";
    public static String PURE_DATETIME_FORMAT = "yyyyMMddHHmmss";

    /**
     * 格式化日期为纯数字格式
     *
     * @param date 日期
     * @return 格式化后的字符串
     */
    public static String simpleDate(Date date) {
        return DateFormatUtils.format(date, PURE_DATE_FORMAT);
    }

    public static String onlyDate(Date date) {
        return DateFormatUtils.format(date, ONLY_DATE_FORMAT);
    }

    /**
     * 获取指定日期的周标识，格式为 yyyyWww
     *
     * @param time 日期
     * @return 周标识字符串
     */
    public static String simpleWeek(Date time) {
        // 转换为 LocalDate
        LocalDate localDate = time.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        // 使用 ISO 标准，强制一周从周一开始
        WeekFields weekFields = WeekFields.ISO;
        // 获取周数
        int weekNumber = localDate.get(weekFields.weekOfWeekBasedYear());
        // 获取年份
        int year = localDate.getYear();
        // 返回格式化字符串
        return String.format("%dW%d", year, weekNumber);
    }

    /**
     * 获取指定日期的月标识，格式为 yyyyMM
     *
     * @param time 日期
     * @return 月标识字符串
     */
    public static String simpleMonth(Date time) {
        LocalDate localDate = time.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return localDate.format(DateTimeFormatter.ofPattern(MONTH_FORMAT));
    }

    public static String formatLocalDateTime(LocalDateTime localDateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PURE_DATE_FORMAT);
        return localDateTime.format(formatter);
    }

    /**
     * 将 LocalDateTime 转为 "yyyyMMdd" 格式的日期字符串
     *
     * @param dateTime 要转换的 LocalDateTime
     * @return 格式化后的日期字符串
     */
    public static String formatToDay(LocalDateTime dateTime) {
        return dateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    }

    /**
     * 获取当前日期对应的周数，格式为 "yyyyWw"（例如 2023W10），并且一周从周一开始
     *
     * @param dateTime 要转换的 LocalDateTime
     * @return 当前日期的周数（格式：yyyyWw）
     */
    public static String formatToWeek(LocalDateTime dateTime) {
        int year = dateTime.getYear();

        WeekFields weekFields = WeekFields.ISO;
        int weekNumber = dateTime.get(weekFields.weekOfWeekBasedYear());  // 获取当前周数

        return String.format("%dW%d", year, weekNumber);
    }

    public static Integer getYear(LocalDateTime localDateTime) {
        return localDateTime.getYear();
    }

    public static Integer getMonth(LocalDateTime localDateTime) {
        return localDateTime.getMonthValue();
    }

    public static Integer getWeek(LocalDateTime localDateTime) {
        return localDateTime.get(WeekFields.ISO.weekOfWeekBasedYear());
    }

    /**
     * 获取当前日期对应的月份，格式为 "yyyyMM"
     *
     * @param dateTime 要转换的 LocalDateTime
     * @return 当前日期的月份（格式：yyyyMM）
     */
    public static String formatToMonth(LocalDateTime dateTime) {
        return dateTime.format(DateTimeFormatter.ofPattern("yyyyMM"));
    }

    public static String formatLocalDate(LocalDate localDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PURE_DATE_FORMAT);
        return localDate.format(formatter);
    }

    public static LocalDate fromMillis(long millis) {
        return Instant.ofEpochMilli(millis)
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
    }

    public static LocalDate fromSeconds(long seconds) {
        return Instant.ofEpochSecond(seconds)
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
    }


    public static String nowLocalDateByYYYYMMDD() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    }

    public static String plusDayByYYYYMMDD(int days) {
        return LocalDate.now().plusDays(days).format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    }


    public static Date fromMillisToDate(long millis) {
        return Date.from(Instant.ofEpochMilli(millis));
    }

    public static Integer getDayOfWeekIndex(Date date) {
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return localDate.getDayOfWeek().getValue() - 1; // 周一为 0，周日为 6
    }

    public static Integer getDayOfMonthIndex(Date date) {
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return localDate.getDayOfMonth() - 1; // 月第一天为 0
    }

    /**
     * 将 LocalDateTime 转换为毫秒级时间戳
     *
     * @param localDateTime 要转换的 LocalDateTime 对象
     * @return 时间戳（毫秒），如果输入为 null，则返回 null
     */
    public static Long toEpochMilli(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return null;
        }
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 将毫秒级时间戳转换为 LocalDateTime
     *
     * @param epochMilli 毫秒级时间戳
     * @return 转换后的 LocalDateTime 对象，如果输入为 null，则返回 null
     */
    public static LocalDateTime fromEpochMilli(Long epochMilli) {
        if (epochMilli == null) {
            return null;
        }
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault());
    }

    /**
     * 根据开始时间和指定的天数计算结束时间，结束时间为指定天数后的那天24点。
     *
     * @param startTime 开始时间
     * @param days      持续天数
     * @return 结束时间
     */
    public static LocalDateTime getEndTime(LocalDateTime startTime, int days) {
        // 计算结束日期，并设置为该日期的24点
        return startTime.plusDays(days).with(LocalTime.MAX);
    }

    /**
     * 根据开始时间和指定的变更天数，获取变更后的0点时间。
     *
     * @param startTime 开始时间
     * @param delayDays 延迟天数（可以是负数，表示提前的天数）
     * @return 变更后的0点时间
     */
    public static LocalDateTime getStartOfDayAfterDelay(LocalDateTime startTime, int delayDays) {
        // 调整日期，延迟天数后
        LocalDateTime adjustedDate = startTime.plusDays(delayDays);

        // 设置为该日期的0点
        return adjustedDate.toLocalDate().atStartOfDay();
    }

    /**
     * 获取某一天的24点的时间戳（毫秒）
     *
     * @param dateTime 指定的时间
     * @return 24点的时间戳（毫秒）
     */
    public static long getEndOfDayTimestamp(LocalDateTime dateTime) {
        LocalDateTime endOfDay = dateTime.with(LocalTime.MAX);
        return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant()).getTime();
    }

    /**
     * 判断一个时间是否在两个时间之间（包括起始时间和结束时间）。
     *
     * @param targetTime  需要判断的时间
     * @param startTime   起始时间
     * @param endTime     结束时间
     * @return 如果 targetTime 在 startTime 和 endTime 之间（含边界），返回 true；否则返回 false
     */
    public static boolean isBetween(LocalDateTime targetTime, LocalDateTime startTime, LocalDateTime endTime) {
        if (targetTime == null || startTime == null || endTime == null) {
            throw new IllegalArgumentException("时间参数不能为 null");
        }

        return !targetTime.isBefore(startTime) && !targetTime.isAfter(endTime);
    }

    public static boolean isBetween(Long targetTime, Long startTime, Long endTime) {
        if (targetTime == null || startTime == null || endTime == null) {
            throw new IllegalArgumentException("时间参数不能为 null");
        }

        return targetTime >= startTime && targetTime <= endTime;
    }

    /**
     * 计算两个 LocalDateTime 对象之间的自然周差
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return 两个日期之间的自然周差
     */
    public static long calculateWeeksDifference(LocalDateTime startDate, LocalDateTime endDate) {
        // 获取开始日期的周一日期
        LocalDate startDateOnly = startDate.toLocalDate();
        LocalDate startOfWeek = startDateOnly.with(DayOfWeek.MONDAY);

        // 获取结束日期的周一日期
        LocalDate endDateOnly = endDate.toLocalDate();
        LocalDate startOfEndWeek = endDateOnly.with(DayOfWeek.MONDAY);

        // 计算两个周的周一日期之间的自然周差
        return ChronoUnit.WEEKS.between(startOfWeek, startOfEndWeek);
    }

}
